iT邦幫忙

2025 iThome 鐵人賽

DAY 5
2
Software Development

ClickHouse 系列:從資料庫底層架構到軟體應用實踐系列 第 5

Day 5|ClickHouse 系列:ReplacingMergeTree 與資料去重機制

  • 分享至 

  • xImage
  •  

「資料重複」是常見且麻煩的問題,尤其是在 ETL Pipeline 或實時資料流匯入(如 Kafka Stream)時,重複資料會嚴重影響統計結果與查詢效率。ClickHouse 提供了一套簡單卻強大的去重機制:ReplacingMergeTree 儲存引擎

本篇文章將帶你深入了解 ReplacingMergeTree 的運作原理、使用場景與 Best Practice。

ReplacingMergeTree 是什麼?

ReplacingMergeTree 是 ClickHouse MergeTree 家族的一員(家族真熱鬧),它能在背景 Merge 操作時,自動根據指定欄位 (如 version 欄位) 將重複資料去除,保留最新版本或最先寫入的那一筆。

運作邏輯:

  1. 在 INSERT 資料時 不會即時去重,資料會以 Data Part 形式寫入磁碟。
  2. 背景 Merge 操作時,根據 Primary Key 進行資料比對,若發現重複記錄(相同 Primary Key),則保留 version 最大值 (或任一筆若無 version)。
  3. 資料去重是 非即時、最終一致性 的,實際去重點發生於 Merge 階段。

ReplacingMergeTree 的語法與範例

CREATE TABLE user_profiles
(
    user_id UInt64,
    profile_version UInt32,
    name String,
    email String
) ENGINE = ReplacingMergeTree(profile_version)
ORDER BY user_id;
  • profile_version 為版本欄位,決定在去重時保留最新版本。
  • 若不指定 version,系統會隨機保留其中一筆(不保證順序),走一個俄羅斯輪盤風格。
INSERT INTO user_profiles VALUES (1, 1, 'Alice', 'alice_v1@example.com');
INSERT INTO user_profiles VALUES (1, 2, 'Alice', 'alice_v2@example.com');
INSERT INTO user_profiles VALUES (2, 1, 'Bob', 'bob@example.com');

查詢時可能會看到重複資料,因為去重尚未透過 Merge 發生:

SELECT * FROM user_profiles WHERE user_id = 1;

結果:

user_id profile_version name email
1 1 Alice alice_v1@example.com
1 2 Alice alice_v2@example.com

進行 FINAL 查詢語法後( FINAL 確保查詢時「讀到最新去重後的結果」):

OPTIMIZE TABLE user_profiles FINAL;
SELECT * FROM user_profiles FINAL WHERE user_id = 1;

OPTIMIZE:強制執行資料片段 (Data Parts) 合併動作,把分散的小 Data Parts 合併成大 Part,並同時觸發資料去重(ReplacingMergeTree)或聚合(SummingMergeTree)等邏輯。

實質影響的是磁碟上的 Data Parts 資料,合併結果是永久性(會寫入磁碟)。

再查詢結果只會剩下 version = 2 的那筆資料。

ReplacingMergeTree 跟 Primary Key 的關係

  • 去重是基於 Primary Key 判斷的,因此建表時的 ORDER BY 欄位(也就是 Primary Key)必須正確設計。
  • ORDER BY 欄位無法唯一識別一筆記錄,則 ReplacingMergeTree 可能會保留錯誤的資料版本。

範例:

ORDER BY (user_id, profile_version)

這種情況下,ReplacingMergeTree 無法自動去重,因為 Primary Key 已包含 version 值,會將每個版本當成不同資料。

正確設計應該是:

ORDER BY user_id

ReplacingMergeTree 使用時機與適用場景

場景 說明
Kafka / Stream 資料流重放去重 多次 Consume、資料重放(At-least-once 保證)情境下自動去重。
批次資料匯入過濾重複 ETL 導入過程中重複載入相同資料,透過背景去重保證資料唯一性。
具版本控制的資料歷史維護 保留最新版本資料,版本號較小的資料會在合併過程中被去除。
資料補正修正 資料寫入後若有誤,透過補寫版本號較大的修正資料覆蓋錯誤記錄。

ReplacingMergeTree v.s. AggregatingMergeTree

特性 ReplacingMergeTree AggregatingMergeTree
核心功能 根據 Primary Key 去重,保留 version 最大的資料 根據 AggregateFunction 資料型別進行合併彙總
版本欄位 可選,無指定時保留任一筆 無需版本欄位
適用場景 重複資料去除、最新版本資料保持 預先彙總的資料庫,如行為統計、即時計數
Merge 過程 合併時將相同主鍵的資料合併為一筆 合併時將 AggregateFunction 聚合運算結果計算出來

ReplacingMergeTree 常見誤區與最佳實踐

常見誤區:

  1. 以為寫入時就會去重錯! ReplacingMergeTree背景合併去重
  2. ORDER BY 選錯欄位 → 主鍵沒選好,去重完全無效。
  3. 期待去重後資料即時查詢結果就正確 → 無強制 Optimize 時可能仍查到重複資料。

最佳實踐:

  • 對於重複資料容忍度低的系統,可在寫入後定期執行 OPTIMIZE TABLE FINAL 來確保資料唯一。
  • 若為實時查詢應用,並且資料版本需求複雜,考慮用 ReplacingMergeTree + Materialized View 實作即時計算最新版本視圖。
  • 設計 Primary Key 時,應僅包含「能唯一識別一筆邏輯記錄」的欄位,不要把 version 放進 ORDER BY

ReplacingMergeTree 與去重效能影響

  • ReplacingMergeTree 的寫入效能與 MergeTree 相近,因為去重是延遲處理的,不影響寫入吞吐量。
  • 背景合併 (Merge) 時會額外進行去重比對,Merge 負載會稍大,但對查詢來說,去重後反而能大幅減少掃描資料量。
  • 資料去重完成後,儲存空間可獲得顯著節省(取決於重複資料比例)。

結語

ReplacingMergeTree 提供了一種無需複雜索引或額外處理邏輯即可實現資料去重的輕量解決方案,非常適合用於 「Data Stream 去重」、「批次匯入防重複」、「版本控制」 等場景。
然而,了解其 去重時機Primary Key 設計要點 是使用這個引擎的關鍵。


上一篇
Day 4|ClickHouse 系列:壓縮技術與 Data Skipping Indexes 如何大幅加速查詢
下一篇
Day 6|ClickHouse 系列:SummingMergeTree 進行資料彙總的應用場景
系列文
ClickHouse 系列:從資料庫底層架構到軟體應用實踐30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言